package com.virtualparadigm.compositor;

import java.io.Serializable;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;

import com.virtualparadigm.identity.Identifiable;

public class OneToOneAggregate<ThisType extends Identifiable, ThatType extends Identifiable> implements Serializable
{
	private static final long serialVersionUID = 1L;
	
	// Why need "wrapped" Aggregate instance?
	//   - b/c aggregate is implemented via composition
	private ThisType instance;
	private OneToOneAggregate<ThatType, ThisType> oneToOneAggregate;

	public OneToOneAggregate(ThisType instance, OneToOneAggregate<ThatType, ThisType> aggregate)
	{
		this.setInstance(instance);
		this.setOneToOneAggregate(aggregate);
	}
	

	public ThisType getInstance()
	{
		return instance;
	}
	private void setInstance(ThisType instance)
	{
		this.instance = instance;
	}
	
	// package protected
	OneToOneAggregate<ThatType, ThisType> getOneToOneAggregate()
	{
		return this.oneToOneAggregate;
	}
	public ThatType getAggregate()
	{
		ThatType typedAggregate = null;
		OneToOneAggregate<ThatType, ThisType> oneToOneAggregate = this.getOneToOneAggregate();
		if(oneToOneAggregate != null)
		{
			typedAggregate = oneToOneAggregate.getInstance();
		}
		return typedAggregate;
	}
	
	// package protected
	boolean setOneToOneAggregate(OneToOneAggregate<ThatType, ThisType> oneToOneAggregate)
	{
		if(this.oneToOneAggregate == null)
		{
			if(oneToOneAggregate != null)
			{
				this.oneToOneAggregate = oneToOneAggregate;
				oneToOneAggregate.setOneToOneAggregate(this);
			}
		}
		else
		{
			if(!this.oneToOneAggregate.equals(oneToOneAggregate))
			{
				OneToOneAggregate<ThatType, ThisType> tempAggregate = this.oneToOneAggregate;
				this.oneToOneAggregate = oneToOneAggregate;
				tempAggregate.setOneToOneAggregate(null);
				if(oneToOneAggregate == null)
				{
//					this.handleRemovedAggregate();
					// dont need to do anything to children with aggregation
				}
				else
				{
					oneToOneAggregate.setOneToOneAggregate(this);
				}
			}
		}
		return true;
	}
	
	public boolean setAggregate(ThatType aggregate)
	{
		OneToOneAggregate<ThatType, ThisType> oneToOneAggregate = null;
		if(aggregate != null)
		{
			oneToOneAggregate = new OneToOneAggregate<ThatType, ThisType>(aggregate, this);
		}
		return this.setOneToOneAggregate(oneToOneAggregate);
	}

	// ================================================
	// UTILITY METHODS
	// ================================================
	public String toString()
	{
//		return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
		return ReflectionToStringBuilder.toString(this, ToStringStyleBuilder.toStringStyle);
	}
	public int hashCode()
	{
		HashCodeBuilder builder = new HashCodeBuilder();
	    builder.append(this.getInstance().getResourceIdentifier());
	    return builder.toHashCode();		
	}
	public boolean equals(Object obj)
	{
		if (obj == null)
		{
			return false;
		}
		if (obj == this)
		{
			return true;
		}
		if (obj.getClass() != getClass())
		{
			return false;
		}
		OneToOneAggregate<ThisType, ThatType> that = (OneToOneAggregate<ThisType, ThatType>)obj;
		EqualsBuilder builder = new EqualsBuilder();
	    builder.append(this.getInstance().getResourceIdentifier(), that.getInstance().getResourceIdentifier());
		return builder.isEquals();
	}



	
}